package info.niwota.fwrite;

import info.niwota.commons.XmppService;
import info.niwota.fwrite.ColorPickView.OnColorChangedListener;
import info.niwota.fwrite.SizePickDialog.OnSizeChangedListener;
import info.niwota.fwrite.img.Holder;
import info.niwota.fwrite.img.HolderActivity;
import info.niwota.fwrite.imglist.MultiSelectImageActivity;

import java.io.File;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.gesture.Gesture;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGestureListener;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.gesture.GestureOverlayView.OnGesturingListener;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

public class BaseActivity extends HolderActivity implements OnClickListener {
	private static final String TAG = "BaseActivity";

	// values from GestureOverlayView source code
	// private float mGestureStrokeLengthThreshold = 50.0f;
	// private float mGestureStrokeSquarenessTreshold = 0.275f;
	// private float mGestureStrokeAngleThreshold = 40.0f;

	//
	private Future<Object> result;
	
	private int progress;

	private final ProgressMonitor progressMonitor = new ProgressMonitor();

	private final class ProgressHandler extends Handler {
		private boolean started;

		private void handleEnd() {
			started = false;
			//
			Object obj = null;
			try {
				obj = result.get();
			} catch (Exception e) {
				error = e;
				e.printStackTrace();
			}
			if (Debug.DEBUG) {
				Log.d(TAG, "ProgressHandler: " + obj);
			}
			
			if (result.isCancelled()) {
				onCanceled();
				return;
			}
			// 
			onPostExecute(obj);
		}
	
		@Override
		public void handleMessage(Message msg) {
			int what = msg.what;
			if (Debug.DEBUG) {
				Log.d(TAG, "ProgressHandler: " + " what: "
						+ what + " progress: " + progress);
			}
			
			if (what == WHAT_START) {
				handler.removeMessages(WHAT_ERROR);
				handler.removeMessages(WHAT_END);
				handler.removeMessages(WHAT_PROGRRESS);
				handler.removeMessages(WHAT_START);
				//
				started = true;
				showBusy(true);
				return;
			}
			
			if (started) {
				//show progress
			}
			
			if (what == WHAT_ERROR) {
				try {
					showBusy(false);
					
					showDialog(DIALOG_CANCEL);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if (what == WHAT_END) {
				try {
					showBusy(false);
					
					handleEnd();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
	
		}

		@Override
		public String toString() {
			return "Message Handler";
		}
	}

	private final ProgressHandler handler = new ProgressHandler();

	private final static int WHAT_END = 3;
	
	private final static int WHAT_ERROR = 4;

	private final static int WHAT_PROGRRESS = 2;

	private final static int WHAT_START = 1;

	private final Worker worker = new Worker();

	private final class Worker implements Callable<Object> {
		
		public Object call() throws Exception {
			try {
				error = null;
				return doWork();
			} catch (Exception e) {
				error = e;
			}
			return error;
		}
	}

	private final ExecutorService executor = Executors
			.newFixedThreadPool(2);
	
	private final class ProgressMonitor implements Runnable {

		private static final long MAX_TIME = 1000 * 60;

		private void endProgress() {
			progress = 100;
			handler.sendEmptyMessage(WHAT_END);
		}

		private void initProgress() {
			progress = 0;
		}
		
		public void run() {
			try {
				if (Debug.DEBUG) {
					Log.d(TAG, "ProgressMonitor");
				}
				cancelIfRunning(false);
				
				result = executor.submit(worker);

				initProgress();
				//
				boolean started = false;
				final long timeout = 500;
				final long maxtime = MAX_TIME;
				long elapsed = 0;
				while (!result.isDone() && !result.isCancelled()) {
					try {
						result.get(timeout, TimeUnit.MILLISECONDS);
					} catch (TimeoutException e) {
						// only show progress after at least x ms
						// e.printStackTrace();
						//
						Log.d(TAG, "timeout: " + elapsed);
						if (!started) {
							started = true;
							startProgress();
						}
						elapsed += timeout;
					}
					if (elapsed > maxtime) {
						Log.d(TAG, "max time exceeded. canceling... "
								+ elapsed);
						result.cancel(true);
						break;
					}
					sendProgress();
				}
				//
				sendProgress();

				if (Debug.DEBUG) {
					Log.d(TAG, "ProgressMonitor done");
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				endProgress();
			}
		}

		private void sendProgress() {
			progress += 10;
			progress = (progress < 100 ? progress : 99); // never complete
			//
			handler.sendEmptyMessage(WHAT_PROGRRESS);
		}

		private void startProgress() {
			handler.sendEmptyMessage(WHAT_START);
		}
	}

	private Future<?> monitor;
	
	protected void cancelIfRunning() {
		//called from UI
		if (sending) {
			sending = false;
			cancelIfRunning(true);
			showBusy(false);
			showDialog(DIALOG_CANCEL);
		}
	}
	
	private void cancelIfRunning(boolean clear) {
		if (clear) {
			showBusy(false);
		}
		
		try {
			if (monitor != null && !monitor.isDone()) {
				monitor.cancel(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			if (result != null && !result.isDone()) {
				result.cancel(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private View mBusy;
	
	protected void showBusy(boolean b) {
		mBusy.setVisibility(b ? View.VISIBLE : View.GONE);
		mBusy.invalidate();
	}
	
	private Object element;

	private boolean sending;

	private Exception error;

	protected Object doWork() throws Exception {
		sending = true;
		if (Debug.DEBUG) {
			Log.d(TAG, "doWork: " + element);
		}
		try {
			if (element instanceof GestureElement) {
				SessionManager.sendGuesture(this, mPeer, (GestureElement) element, mAutoSpace);
			} else if (element instanceof ResourceImageElement) {
				SessionManager.sendSmiley(this, mPeer, ((ResourceImageElement) element).resource);
			} else if (element instanceof ImageElement) {
				SessionManager.sendImage(this, mPeer, (ImageElement) element);
			} else if (element instanceof TextElement) {
				SessionManager.sendText(this, mPeer, (TextElement) element);
			} else if (element instanceof String){
				SessionManager.sendKey(this, mPeer, (String) element);
			}
		} catch (Exception e) {
			throw e;
		} finally {
			sending = false;
		}
		return null;
	}
	
	protected void onCanceled() {
		showDialog(DIALOG_CANCEL);
	}
	
	protected void onPostExecute(Object obj) {
		if (error != null) {
			showDialog(DIALOG_CANCEL);
		}
	}
	
	protected boolean isSending() {
		return sending;
	}

	private void sendMessage(Object obj) {
		try {
			showBusy(true);
		
			element = obj;
			monitor = executor.submit(progressMonitor);
		} catch (Exception e) {
			showError(e);
			
			e.printStackTrace();
		}
	}
	
	private void resendMessage() {
		try {
			showBusy(true);
		
			monitor = executor.submit(progressMonitor);
		} catch (Exception e) {
			showError(e);
			
			e.printStackTrace();
		}
	}
	//
	
	public float mScale;

	private class FlingListener extends GestureDetector.SimpleOnGestureListener {
		@Override
		public boolean onDoubleTap(MotionEvent e) {
			float x = e.getX();
			float y = e.getY();
			if (Debug.DEBUG) {
				Log.d(TAG, "onDoubleTap  x: " + x + " y: " + y);
			}
			DocumentView iv = mDocument;
			float scale = iv.getScale();
			//
			if (iv.isViewMode()) {
				mScale = scale;
				//
				setViewMode(false);
				
				iv.zoomTo(1.0f);
				iv.scrollTo(0, 0);
			} else {
				setViewMode(true);
				
				iv.zoomToPoint(Math.max(1.50f, mScale), x, y);

				// iv.zoomTo(Math.max(1.50f, mScale));
				// iv.scrollTo((int) x, (int) y);
			}

			iv.invalidate();
			return true;
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx,
				float dy) {
			// if (Debug.DEBUG) {
			// Log.d(TAG, "onScroll dx: " + dx + " dy: " + dy);
			// }
			DocumentView iv = mDocument;
			GestureOverlayView ol = mOverlay;
			// float scale = iv.getScale();
			// && scale > 1.0f
			if (iv.isViewMode()) {
				if (Debug.DEBUG) {
					Log.d(TAG, "onScroll dx: " + dx + " dy: " + dy);
				}

				ol.clear(false);
				//
				iv.scrollBy((int) dx, (int) dy);
				// iv.postTranslateCenter(-dx, -dy);

				return true;
			}
			return false;
		}

		@Override
		public boolean onSingleTapConfirmed(MotionEvent e) {
			float x = e.getX();
			float y = e.getY();
			if (Debug.DEBUG) {
				Log.d(TAG, "onSingleTapConfirmed  x: " + x + " y: " + y);
			}
			mOverlay.clear(false);
			if (mDocument.isViewMode()) {
				scheduleShowControls();
			} else {
				doKey(R.id.keyboard_sp);
			}
			return true;
		}

		@Override
		public void onLongPress(MotionEvent e) {
			float x = e.getX();
			float y = e.getY();
			//
			if (Debug.DEBUG) {
				Log.d(TAG, "onLongPress  x: " + x + " y: " + y);
			}
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			return true;
		}
	}

	private class DrawListener implements OnGesturingListener,
			OnGestureListener, OnGesturePerformedListener {

		public void onGesture(GestureOverlayView overlay, MotionEvent event) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGesture overlay: " + overlay + " event: " + event);
			}
		}

		public void onGestureCancelled(GestureOverlayView overlay,
				MotionEvent event) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGestureCancelled overlay: " + overlay
						+ " event: " + event);
			}
		}

		public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGestureEnded overlay: " + overlay + " event: "
						+ event);
			}
		}

		public void onGesturePerformed(GestureOverlayView overlay,
				Gesture gesture) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGesturePerformed gesture: " + gesture);
			}
			DocumentView iv = mDocument;
			// float scale = iv.getScale();
			if (!isSending() && !iv.isViewMode() && gesture != null
					&& gesture.getStrokesCount() > 0) {
				//
				addGestureElement(gesture, null);
			}
			overlay.clear(true);
		}

		public void onGestureStarted(GestureOverlayView overlay,
				MotionEvent event) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGestureStarted overlay: " + overlay + " event: "
						+ event);
			}
		}

		public void onGesturingEnded(GestureOverlayView overlay) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGesturingEnded " + overlay);
			}
		}

		public void onGesturingStarted(GestureOverlayView overlay) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onGesturingStarted " + overlay);
			}
		}
	}

	private class DocumentHolder implements Holder {

		public int getCount() {
			return 1;
		}

		public ImageViewTouch getDocumentView() {
			return mDocument;
		}

//		public View getNextView() {
//			return null;
//		}

		public View getOwner() {
			return mOwnerView;
		}

		public int getPosition() {
			return 0;
		}

//		public View getPrevView() {
//			return null;
//		}

		public View getRoot() {
			return mRootView;
		}

		public boolean hasDocument() {
			return true;
		}

		public boolean isPaused() {
			return mPaused;
		}

		public void showDocument(int pos) {
			// refreshDocument();
		}

	}

	private static final int DEFAULT_COLOR = 0xffffffff;

	private static final int DEFAULT_INSET = 2;

	// private static final int DEFAULT_SIZE = 64;

	private static final float DEFAULT_STROKE_WIDTH = 12.0f;

	private static final int DIALOG_FORMAT_COLOR = 4;

	private static final int DIALOG_FORMAT_SIZE = 3;

	private static final int DIALOG_CLEAR = 6;

	//private static final int DIALOG_BUSY_WAIT = 7;

	private static final int DIALOG_INSERT_FACE = 5;
	
	private static final int DIALOG_CANCEL = 8;

	// private static final float LENGTH_THRESHOLD = 120.0f;

	private static final int RC_PICK_IMAGE = 1;

	private static final int RC_SEND = 3;

	private static final int RC_PICK_BG = 7;

	private static final int DIALOG_MORE = 9;
	
	private static final int RC_SWITCH_SESSION = 5;
	
	//
//	private static final int ACTION_NONE = -1;
//	
//	private static final int ACTION_SAVE = 0;
//
//	private static final int ACTION_SEND = 1;
//
//	private static final int ACTION_CHAT = 2;
	
	//private int action = ACTION_NONE;


	// private static final float CONTINUATION_THRESHOLD = 10.0f;

	/**
	 * 
	 * @param view
	 *            true viewing mode false editing mode
	 */
	private void setViewMode(boolean view) {
		// hide or show editing actions
		mDocument.setViewMode(view);
		updateOverlay(view);
		
		//
		mToolbar.setVisibility(view ? View.GONE : View.VISIBLE);
		mToolbar.setEnabled(!view);
		
		mKeyboard.setVisibility(view ? View.GONE : View.VISIBLE);
		mKeyboard.setEnabled(!view);
		
		mKeyboard2.setVisibility(View.GONE);
		mKeyboard2.setEnabled(false);

		//
		mBusy.setVisibility(View.GONE);
	}

	/**
	 * 
	 * @param view only true|false
	 */
	private void updateOverlay(boolean view) {
		GestureOverlayView ol = mOverlay;
		if (view) {
			ol.setGestureVisible(false);
			ol.setEnabled(false);
		} else {
			ol.setGestureVisible(true);
			ol.setEnabled(true);

			ol.bringToFront();
		}
		ol.clear(false);
	}

	private void scheduleShowControls() {
		showControls();
		scheduleDismissControls();
	}

	@Override
	protected void controlsHiding() {
		mOverlay.setGestureVisible(true);
	}

	@Override
	protected void controlsShowing() {
		mOverlay.setGestureVisible(false);
	}

	private ImageButton mActions;

	private int mColor = DEFAULT_COLOR;

	private Display mDisplay;

	protected DocumentView mDocument;

	private float mGestureStrokeAngleThreshold = 0.0f;

	private float mGestureStrokeLengthThreshold = 10.0f; // 0.0f

	private float mGestureStrokeSquarenessTreshold = 0.0f;

	private float mGestureStrokeWidth = DEFAULT_STROKE_WIDTH;

	private int mInset = DEFAULT_INSET;

	private Button mKeyBS;

	protected Button mKeyMore;

	private Button mKeyLF;

	protected Button mKeyDone;

	private Button mKeySmiley;

	private Button mKeySP;

	private Line[] mLines = new Line[2]; // chat one on one, no group

	private GestureOverlayView mOverlay;

	private int mLinesize = Preferences.DEFAULT_LINESIZE;

	private boolean mAutoSpace;

	private View mOwnerView;

	private View mRootView;

	protected TextView mTitle;

	private View mActionFormatSize;

	private View mActionFormatColor;

	private boolean mPaused;

	private View mActionClear;

	private int screenWidth;

	private int screenHeight;

	private String mPeer = null;

	private View mToolbar;

	private View mKeyboard;

	private View mKeyboard2;

	private EditText mTextEntry;

	private Button mKeyLock;

	private Button mKeyEnter;

	private Button mKeyText;

	private boolean enterLock;

	private Button mKeyImage;

	private View mActionSetBG;
	
	private Document currentDocument;

	protected void addBlankElement(String peer) {
		Element el = new BlankElement();
		
		addElement(el, peer);
	}

	protected void addElement(Element el, String peer) {
		int idx = mapLine(peer);
		if (mLines[idx] == null) {
			mLines[idx] = new Line(peer, screenWidth, screenHeight/mLinesize);
			mDocument.addLine(mLines[idx]);
		}
		mLines[idx].add(el);
		mDocument.setActiveLine(mLines[idx]);

		invalidateDocument();
	}

	private int mapLine(String peer) {
		//
		return peer == null ? 0 : 1;
	}

	protected void addGestureElement(Gesture gesture, String peer) {
		if (gesture == null) {
			return;
		}
		GestureElement el = createElement(gesture);
		addElement(el, peer);
		//
		if (mAutoSpace) {
			addBlankElement(peer);
		}
		
		//
		if (mPeer != null) {
			sendMessage(el);
		}
	}

	protected void addImage(File file) {
		Utils.addImage(getContentResolver(), file, file.getName());
	}

	protected void addSmileyElement(int icon, String peer) {
		Element el = new ResourceImageElement(icon);
		addElement(el, peer);
		
		if (mPeer != null) {
			sendMessage(el);
		}
	}

	protected void addTextElement(int type, String text, String peer) {
		Element el = new TextElement(type, text, mColor, TextElement.DEFAULT_SIZE);
		addElement(el, peer);
		
		if (mPeer != null) {
			sendMessage(el);
		}
	}

	protected void addImageElement(Uri uri, String peer) {
		if (Debug.DEBUG) {
			Log.d(TAG, "addImageElement uri: " + uri);
		}
		String p = Utils.getDataPath(this, uri);
		ImageElement el = new ImageElement(p);
		addElement(el, peer);
		
		if (mPeer != null) {
			sendMessage(el);
		}
	}

	private GestureElement createElement(Gesture g) {
		GestureElement el = new GestureElement(g);

		el.setInset(mInset);
		el.setColor(mColor);

		RectF rf = g.getBoundingBox();
		//
		float r = getMaxLines();
		float rh = rf.height();
		float rw = rf.width();
		//
		int x = (int) (rf.left / r);
		int y = (int) (rf.top / r);
		int w = (int) (rw / r);
		int h = (int) (rh / r);
		int min = mInset * 2 + 1;
		w = Math.max(w, min);
		h = Math.max(h, min);
		
		if (Debug.DEBUG) {
			Log.d(TAG, " rh: " + rh + " rw: " + rw + " x: " + x + " : " + y
					+ " h: " + h + " w: " + w);
		}
		
		el.setWidth(w);
		el.setHeight(h);
		return el;
	}

	private Dialog createClearDialog() {
		final String[] items = getResources().getStringArray(
				R.array.menu_clear_entries);
		return new AlertDialog.Builder(this).setItems(items,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						if (which == 0) {
							// line 
							// clear own line only
							if (mLines[0] != null) {
								mLines[0].clear();
								invalidateDocument();
							}
						} else if (which == 1) {
							// clear all
							clearDocument();
						}
					}
				}).create();
	}

	private Dialog createFormatColorDialog() {
		return new ColorPickDialog(this, new OnColorChangedListener() {
			public void colorChanged(int color) {
				mColor = color;
				mOverlay.setGestureColor(color);
				mDocument.setPenColor(color);
				invalidateDocument();
			}
		}, mColor);
	}

	private Dialog createFormatSizeDialog() {
		return new SizePickDialog(this, new OnSizeChangedListener() {
			public void sizeChanged(int size) {
				mGestureStrokeWidth = size;
				mOverlay.setGestureStrokeWidth(size);
				mDocument.setPenWidth(size);
				invalidateDocument();
			}
		}, (int) mGestureStrokeWidth);
	}

	private Dialog createSmileyDialog() {
		return MenuDialogs.createFaceDialog(this, R.string.action_smiley,
				new MenuCallback() {
					public void perform(MenuInfo mi) {
						int res = mi.icon;
						addSmileyElement(res, null);
					}
				});
	}
	
	private Dialog createMoreDialog() {
		return MenuDialogs.createMoreDialog(this, R.string.action_more,
				new MenuCallback() {
					public void perform(MenuInfo mi) {
						switch(mi.id) {
						case R.string.action_flag:
							//doShowFlags(currentDocument);
							break;
						case R.string.action_alert:
							break;
						case R.string.action_attach:
							break;
						case R.string.action_tag:
							break;
						}
					}
				});
	}
	
	private Dialog createCancelDialog() {
		DialogInterface.OnClickListener cl = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				switch (which) {
				case DialogInterface.BUTTON_POSITIVE:
					resendMessage();
					break;
				case DialogInterface.BUTTON_NEGATIVE:
					undoElement();
					break;
				default:
				}
			}
		};
		AlertDialog.Builder b = new AlertDialog.Builder(this);
		b.setTitle("");
		b.setMessage(R.string.text_not_delivered);
		b.setPositiveButton(R.string.action_retry, cl);
		b.setNegativeButton(R.string.action_discard, cl);
		//
		return b.create();
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		return super.dispatchTouchEvent(ev);
	}

	protected void addLineFeed(String peer) {
		int idx = mapLine(peer);
		
		mLines[idx] = new Line(peer,screenWidth, screenHeight/mLinesize);
		mDocument.addLine(mLines[idx]);
		
		mDocument.setActiveLine(mLines[idx]);
		
		invalidateDocument();
	}

	private void doPickImage() {
		try {
			Intent i = new Intent(Intent.ACTION_GET_CONTENT);
			i.setType("image/*");
			// startActivityForResult(i, RC_PICK);
			final CharSequence title = getText(R.string.text_pick);
			Intent ci = Intent.createChooser(i, title);
			startActivityForResult(ci, RC_PICK_IMAGE);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void save() {
		try {
			if (Debug.DEBUG) {
				Log.d(TAG, "doSave");
			}
			//
			if (currentDocument != null) {
				
				//String msg = getString(R.string.text_saved, "");
				//Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
				
				currentDocument.save(mDocument);
			}
			//
			clearDocument();
			
		} catch (Throwable e) {
			showError(e);
		}
	}

	private File saveAsImage(boolean show) {
		File file = mDocument.capture();	
		
		if (Debug.DEBUG) {
			Log.d(TAG, "saveAsImage file: " + file);
		}
		
		addImage(file);
		clearDocument();
		
		if (show) {
			String msg = getString(R.string.text_saved, file.getPath());
			Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
		}
		
		return file;
	}

	protected void doArchive() {
		try {
			saveAsImage(true);
		} catch (Error e) {
			showError(e);
			
			e.printStackTrace();
		}
	}

	protected void doSend() {
		try {
			File file = saveAsImage(false);
			
			final String subject = getString(R.string.text_greeting_subject);
			final String body = getString(R.string.text_greeting_body);
			
			Uri uri = Utils.toUri(file);
			Intent i = Utils.toShareIntent(subject, body, uri);
			Intent si = Intent.createChooser(i, getText(R.string.text_send));
			startActivityForResult(si, RC_SEND);
			
		} catch (Exception e) {
			showError(e);
		}
	}

	protected void clearDocument() {
		for (int i = 0; i < mLines.length; i++) {
			mLines[i] = null;
		}
		if (mDocument != null) {
			mDocument.clearLines();
			mDocument.invalidate();
		}
	}

	private int getMaxLines() {
		int w = mDisplay.getWidth();
		int h = mDisplay.getHeight();
		int max = h / Math.max(mLinesize, Preferences.DEFAULT_LINESIZE);
		if (Debug.DEBUG) {
			Log.d(TAG, "getMaxLines: " + max + " w: " + w + " h: " + h);
		}
		return max;
	}

	private void initDocument() {
		final DocumentView iv = mDocument;
		if (iv == null) {
			return;
		}
		iv.setPenColor(mColor);
		iv.setPenWidth(mGestureStrokeWidth);
		iv.setViewMode(false);
		iv.clear();
		//
		iv.invalidate();
	}

	protected boolean isPickIntent() {
		Intent i = getIntent();
		String act = i.getAction();
		boolean b = (Intent.ACTION_PICK.equals(act) || Intent.ACTION_GET_CONTENT
				.equals(act));
		if (Debug.DEBUG) {
			Log.d(TAG, "isPickIntent " + b + ":" + act + ":" + i);
		}
		return b;
	}

//	protected void setTemplate() {
////		if (mBackground != null) {
////			return;
////		}
//		//showDialog(DIALOG_BUSY_WAIT);
//		try {
//			mBackground = Utils.makeTemplate(this); // R.drawable.empty
//													// R.drawable.empty
//
//			// Bitmap bm = BitmapFactory.decodeResource(getResources(),
//			// R.drawable.wallpaper_jellyfish);
//			// mDocument.setImageRotateBitmapResetBase(new RotateBitmap(bm),
//			// false);
//
//			mDocument.setImageBitmapResetBase(mBackground);
//
//			// mDocument.setAdjustViewBounds(false);
//			// mDocument.setScaleType(ScaleType.FIT_CENTER);
//			//
//			refreshDocument();
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			//dismissDialog(DIALOG_BUSY_WAIT);
//		}
//	}

	private void initOverlay() {
		DrawListener gp = new DrawListener();
		mOverlay.addOnGesturePerformedListener(gp);
	
		mOverlay.setDescendantFocusability(GestureOverlayView.FOCUS_BLOCK_DESCENDANTS);
		//
		mOverlay.setGestureStrokeAngleThreshold(mGestureStrokeAngleThreshold);
		mOverlay.setGestureStrokeLengthThreshold(mGestureStrokeLengthThreshold);
		mOverlay.setGestureStrokeSquarenessTreshold(mGestureStrokeSquarenessTreshold);
		//
		resetOverlay(mOverlay);
	}

	protected void initRes() {
		mDisplay = ((WindowManager) getSystemService(WINDOW_SERVICE))
				.getDefaultDisplay();

		mOverlay = (GestureOverlayView) findViewById(R.id.overlay);
		
		mDocument = (DocumentView) findViewById(R.id.document);
		mDocument.setLinesize(mLinesize);
		mDocument.setBackgroundResource(R.drawable.background_black);
		
		mOwnerView = findViewById(R.id.document_holder);
		mRootView = findViewById(R.id.document_root);

		//
		mBusy = findViewById(R.id.busy);
		mBusy.setVisibility(View.GONE);

		// title / peer id
		mTitle = (TextView) findViewById(R.id.title);
		mTitle.setText(R.string.application_desc);
		mTitle.setOnClickListener(this);

		mActionFormatSize = findViewById(R.id.actions_format_size);
		mActionFormatColor = findViewById(R.id.actions_format_color);
		mActionClear = findViewById(R.id.actions_clear);
		mActionSetBG = findViewById(R.id.actions_set_bg);
		
		mToolbar = findViewById(R.id.actions_toolbar);
		mKeyboard = findViewById(R.id.keyboard);
		mKeyboard2 = findViewById(R.id.keyboard2);
		mToolbar.setVisibility(View.VISIBLE);
		mKeyboard.setVisibility(View.VISIBLE);
		mKeyboard2.setVisibility(View.GONE);

		mActionFormatSize.setOnClickListener(this);
		mActionFormatColor.setOnClickListener(this);
		mActionClear.setOnClickListener(this);
		mActionSetBG.setOnClickListener(this);
		
		//
		mKeyBS = (Button) findViewById(R.id.keyboard_bs);
		mKeySP = (Button) findViewById(R.id.keyboard_sp);
		mKeyMore = (Button) findViewById(R.id.keyboard_more);
		mKeyLF = (Button) findViewById(R.id.keyboard_lf);
		mKeySmiley = (Button) findViewById(R.id.keyboard_smiley);
		mKeyText = (Button) findViewById(R.id.keyboard_text);
		mKeyImage = (Button) findViewById(R.id.keyboard_img);
		mKeyDone = (Button) findViewById(R.id.keyboard_done);

		mKeyDone.setVisibility(View.VISIBLE);

		//
		mKeyBS.setOnClickListener(this);
		mKeySP.setOnClickListener(this);
		mKeyMore.setOnClickListener(this);
		mKeyLF.setOnClickListener(this);
		mKeySmiley.setOnClickListener(this);
		mKeyText.setOnClickListener(this);
		mKeyImage.setOnClickListener(this);
		mKeyDone.setOnClickListener(this);
		//
		mActions = (ImageButton) findViewById(R.id.menu_actions);
		mActions.setOnClickListener(this);
		
		//keyboard2/text
		mKeyLock = (Button) findViewById(R.id.keyboard2_lock);
		mKeyEnter = (Button) findViewById(R.id.keyboard2_enter);
		mTextEntry = (EditText) findViewById(R.id.keyboard2_entry);
		
		//
		mKeyLock.setOnClickListener(this);
		mKeyEnter.setOnClickListener(this);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onActivityResult requestCode: " + requestCode
					+ " resultCode: " + resultCode + " == " + RESULT_OK
					+ " data: " + data);
		}
		if (resultCode == RESULT_CANCELED) {
			return;
		}
		try {
			switch (requestCode) {
			case RC_PICK_IMAGE:
				doImagePicked(data);
				break;
			case RC_PICK_BG:
				doSetBackground(data);
				break;
			case RC_SEND:
				doSent(data);
				break;
			default:
			}
		} catch (Exception e) {
			showError(e);
		}
	}

	private void doSetBackground(Intent data) {
		Uri uri = null;
	
		List<Uri> list = data
				.getParcelableArrayListExtra(MultiSelectImageActivity.EXTRA_URIS);

		if (list != null && list.size() > 0) {
			uri = list.get(0);
		} else {
			uri = data.getData();
		}
		
		if (Debug.DEBUG) {
			Log.d(TAG, "doSetBackground uri: " + uri);
		}
		
		if (uri != null) {
			mDocument.setBackgroundUri(uri);
		}
	}

	protected void setPeerStatus(String peer, int status) {
		mPeer = peer;
		mTitle.setText(peer);

		mKeyDone.setText(R.string.keyboard_key_close);
		
		//mKeySend.setVisibility(View.GONE);
		//mKeyDone.setVisibility(View.VISIBLE);
		
		setPeerStatus(status);
	}
	
	protected void setPeerStatus(int status) {
		int icon = R.drawable.peer_status_none;
		switch (status) {
		case XmppService.PEER_STATUS_OFF:
			icon = R.drawable.peer_status_off;
			break;
		case XmppService.PEER_STATUS_ONLINE:
			icon = R.drawable.peer_status_off; //show as off
			break;
		case XmppService.PEER_STATUS_AVAIL:
			icon = R.drawable.peer_status_avail;
			break;
		case -1:
			icon = R.drawable.peer_status_write;
			break;
		case -2:
			icon = R.drawable.alert16;
			break;
		default:
			icon = R.drawable.peer_status_none;
		}
		setPeerStatusImage(icon);
	}

	protected void setPeerStatusImage(int img) {
		mTitle.setCompoundDrawablesWithIntrinsicBounds(img, 0, 0,
				R.drawable.divider_horizontal_light);
	}
	
	private void doImagePicked(Intent data) {
		// pick
		Uri uri = data.getData();
		if (Debug.DEBUG) {
			Log.d(TAG, "doAdd uri: " + uri);
		}

		// multi select
		List<Uri> list = data
				.getParcelableArrayListExtra(MultiSelectImageActivity.EXTRA_URIS);
		if (Debug.DEBUG) {
			Log.d(TAG, "doAdd list: " + list);
		}
		//
		if (list != null) {
			for (Uri i : list) {
				addImageElement(i, null);
			}
		} else {
			if (uri != null) {
				addImageElement(uri, null);
				return;
			}
		}
	}

	private void doSent(Intent data) {
		if (Debug.DEBUG) {
			Log.d(TAG, "doSent data: " + data);
		}
	}

	public void onClick(View v) {
		int id = v.getId();
		if (Debug.DEBUG) {
			Log.d(TAG, "onClick id: " + id);
		}
		
		switch(id) {
		case R.id.keyboard_bs:
			doKey(id);
			return;
		case R.id.keyboard_lf:
			doKey(id);
			return;
		case R.id.keyboard_sp:
			doKey(id);
			return;
		case R.id.keyboard_smiley:
			showDialog(DIALOG_INSERT_FACE);
			return;
		case R.id.keyboard_text:
			doShowTextEntry(true);
			return;
		case R.id.keyboard_done:
			doDone();
			return;
		case R.id.actions_format_color:
			showDialog(DIALOG_FORMAT_COLOR);
			return;
		case R.id.actions_format_size:
			showDialog(DIALOG_FORMAT_SIZE);
			return;
		case R.id.actions_clear:
			showDialog(DIALOG_CLEAR);
			return;
		case R.id.keyboard2_lock:
			//toggle text entry lock
			if (enterLock) {
				doShowTextEntry(false);
			} else {
				enterLock = true;
				mKeyLock.setText("X");
			}
			return;
		case R.id.keyboard2_enter:
			doTextEnter();
			return;
		case R.id.keyboard_img:
			doPickImage();
			return;
		case R.id.keyboard_more:
			showDialog(DIALOG_MORE);
			return;
		case R.id.actions_set_bg:
			doPickBackGround();
			return;
		}
		
		if (Debug.DEBUG) {
			Log.d(TAG, "onClick currentDocument: " + currentDocument);
		}
		
		saveIfChanged(currentDocument);
		
		switch(id) {
		case R.id.title:
			doListSessions();
			break;
		default:
			if (Debug.DEBUG) {
				Log.d(TAG, "onClick default: "+ id);
			}
		}
	}

	private void doListSessions() {
		Intent i = new Intent(this, SessionListActivity.class);
		startActivityForResult(i, RC_SWITCH_SESSION);
	}
	
	private void doPickBackGround() {
		try {
			Intent i = new Intent(Intent.ACTION_GET_CONTENT);
			i.setType("image/*");

			final CharSequence title = getText(R.string.text_pick);
			Intent ci = Intent.createChooser(i, title);
			startActivityForResult(ci, RC_PICK_BG);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void doShowTextEntry(boolean b) {
		mKeyLock.setText(".");
		enterLock = false;
		//
		mKeyboard.setVisibility(b ? View.GONE : View.VISIBLE);
		mKeyboard2.setVisibility(!b ? View.GONE : View.VISIBLE);
		
		mTextEntry.setEnabled(b);
		//
		GestureOverlayView ol = mOverlay;
		if (b) {
			ol.setGestureVisible(false);
			ol.setEnabled(false);
			ol.setDescendantFocusability(GestureOverlayView.FOCUS_AFTER_DESCENDANTS);
		} else {
			ol.setGestureVisible(true);
			ol.setEnabled(true);
			ol.setDescendantFocusability(GestureOverlayView.FOCUS_BLOCK_DESCENDANTS);
			ol.bringToFront();
		}
	}

	private void doTextEnter() {
		final String text = mTextEntry.getText().toString();
		if (!TextUtils.isEmpty(text)) {
			addTextElement(TextElement.TYPE_PLAIN, text, null);
		}
		mTextEntry.setText(null);
		
		if (enterLock) {
			addLineFeed(null);
		} else {
			if (mAutoSpace) {
				addBlankElement(null);
			}
			
			doShowTextEntry(false);
		}
	}
	
	protected void doDone() {
		if (Debug.DEBUG) {
			Log.d(TAG, "doDone");
		}
		
//		int action = -1;
//		try {
//			action = (Integer) mKeyDone.getTag();
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//		if (Debug.DEBUG) {
//			Log.d(TAG, "doDone: " + action);
//		}
//		
//		switch(action) {
//		case R.string.keyboard_key_save:
//			doSave();
//			break;
//		case R.string.keyboard_key_send:
//			doSend();
//			break;
//		case R.string.keyboard_key_close:
//			closeSession();
//			break;
//		default:
//		}
	}

	private void doKey(int id) {
		switch(id) {
		case R.id.keyboard_bs:
			removeElement(null);
			break;
		case R.id.keyboard_lf:
			addLineFeed(null);
			break;
		case R.id.keyboard_sp:
			addBlankElement(null);
			break;
		default:
		}
		
		String key = KeyHelper.map(id);
		//TODO check action?
		if (key != null && mPeer != null) {
			sendMessage(key);
		}
	}

	protected void closeSession() {
		String peer = mPeer;
		if (peer != null) {
			SessionManager.doneSession(peer);
		}
		Preferences.putPeer(this, null);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreate:" + savedInstanceState);
		}
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_NO_TITLE);
		//
		setContentView(R.layout.write);
		
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
		//
		initRes();

		initDocument();
		
		initOverlay();
		//
		if (Preferences.enableStatusbar(this)) {
			StatusHelper.addNotification(this);
		}
		//
		initControls();
		//
		init(savedInstanceState);
	}

	protected void init(Bundle savedInstanceState) {
		//default values
		mLinesize = Preferences.getEditLinesize(this);
		mAutoSpace = Preferences.enableAutoSpace(this);
		
		WindowManager w = getWindowManager();
		Display d = w.getDefaultDisplay();
		screenWidth = d.getWidth();
		screenHeight = d.getHeight();
		
		if (Debug.DEBUG) {
			Log.d(TAG, "Screen width: " + screenWidth + " height: "
					+ screenHeight);
		}
		
		mPeer = Preferences.getPeer(this, null);
		if (mPeer != null) {
			loadDocument(mPeer, 0);
		}
	}

	private void initControls() {
		Holder holder = new DocumentHolder();
		GestureDetector gd = new GestureDetector(this, new FlingListener());
		initControls(holder, gd);
	}

	protected boolean hasContent() {
		return (mDocument != null && !mDocument.isEmpty());
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_FORMAT_COLOR:
			return createFormatColorDialog();
		case DIALOG_FORMAT_SIZE:
			return createFormatSizeDialog();
		case DIALOG_INSERT_FACE:
			return createSmileyDialog();
		case DIALOG_CLEAR:
			return createClearDialog();
		case DIALOG_CANCEL:
			return createCancelDialog();
		case DIALOG_MORE:
			return createMoreDialog();
		default:
		}
		return super.onCreateDialog(id);
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		Issues.fixIssue3674(dialog.getWindow());
	
		switch (id) {
		case DIALOG_FORMAT_SIZE:
			SizePickDialog sd = (SizePickDialog) dialog;
			sd.setColor(mColor);
			break;
		case DIALOG_CANCEL:
			final CharSequence def = getText(R.string.text_not_delivered);
			CharSequence msg = (error != null ? error.getLocalizedMessage() : def);
			((AlertDialog) dialog).setMessage(msg == null ? def : msg);
			break;
		default:
		}
		super.onPrepareDialog(id, dialog);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onRestoreInstanceState peer: " + mPeer);
		}
		try {
			super.onRestoreInstanceState(savedInstanceState);
	
			// overlay
			final Gesture g = savedInstanceState.getParcelable("gesture");
			//
			// (GestureOverlayView) findViewById(R.id.overlay);
			final GestureOverlayView overlay = mOverlay;
			overlay.post(new Runnable() {
				public void run() {
					resetOverlay(overlay, g);
				}
			});
		} catch (Throwable e) {
			//
			showError(e);
			e.printStackTrace();
		}
	}

	@Override
	protected void onPause() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onPause peer: " + mPeer);
		}
		super.onPause();
		
		saveIfChanged(currentDocument);
		
		mPaused = true;
		
		Preferences.putPeer(this, mPeer);
		//
		final boolean composing = (mDocument != null && currentDocument == null); //compose or chat
		
		if (composing) {
			try {
				String peer = mPeer == null ? "draft" : mPeer;
				DocumentState s = new DocumentState();
				s.peer = mPeer;
			
				s.strokeWidth = mGestureStrokeWidth;
				s.color = mColor;
				s.inset = mInset;
				s.linesize = mLinesize;
				s.autospace = mAutoSpace;
				
				mDocument.state = s;
				
				SessionManager.saveSession(peer, mDocument);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "onResume peer: " + mPeer);
		}
		mPaused = false;
	}

	protected void loadDocument(String peer, int status) {
		if (Debug.DEBUG) {
			Log.d(TAG, "loadDocument peer: " + peer);
		}
		//
		cancelIfRunning(true);
		
		setPeerStatus(peer, status);
		
		//
		mPeer = peer;
		Preferences.putPeer(this, peer);

		try {
			clearDocument();
			//
			SessionManager.restoreSession((peer == null ? "draft" : peer), mDocument);
			DocumentState s = mDocument.state;
			
			if (s != null) {
				mGestureStrokeWidth = s.strokeWidth;
				mColor = s.color;
				mInset = s.inset;
				mLinesize = s.linesize;
				mAutoSpace = s.autospace;
			} else {
				//
				mGestureStrokeWidth = DEFAULT_STROKE_WIDTH;
				mColor = DEFAULT_COLOR;
				mInset = DEFAULT_INSET;
				mLinesize = Preferences.getEditLinesize(this);
				mAutoSpace = Preferences.enableAutoSpace(this);
			}
			
			mOverlay.setGestureColor(mColor);
			mOverlay.setGestureStrokeWidth(mGestureStrokeWidth);
			
			mDocument.setPenColor(mColor);
			mDocument.setPenWidth(mGestureStrokeWidth);
			mDocument.setViewMode(false);
			
			//
			mLines[0] = mDocument.lastLine(null);
			
			mDocument.invalidate();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onSaveInstanceState peer: " + mPeer);
		}
		try {
			super.onSaveInstanceState(outState);
			
			//
			outState.putString("peer", mPeer);
			
			//
			Gesture g = mOverlay.getGesture();
			if (g != null) {
				outState.putParcelable("gesture", g);
			}
			
		} catch (Throwable e) {
			showError(e);
			e.printStackTrace();
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return super.onTouchEvent(event);
	}

	protected void removeElement(String peer) {
		int idx = mapLine(peer);
		
		if (mLines[idx] != null) {
			if (mLines[idx].count() == 0) {
				mDocument.removeLastLine(peer);
			}
			mLines[idx] = mDocument.lastLine(peer);
			mDocument.setActiveLine(mLines[idx]);
			if (mLines[idx] != null) {
				mLines[idx].remove();
				invalidateDocument();
			}
		}
	}
	
	protected void undoElement() {
		showBusy(false);
		
		removeElement(null);
		if (mAutoSpace) {
			removeElement(null);
		}
	}

	protected void invalidateDocument() {
		mDocument.invalidate();
		if (currentDocument != null) {
			currentDocument.setChanged(true);
		}
	}

	private void resetOverlay(GestureOverlayView v) {
		v.setGestureVisible(true);
		v.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);
		v.setGestureColor(mColor);
		v.setGestureStrokeWidth(mGestureStrokeWidth);
	}

	private void resetOverlay(GestureOverlayView v, Gesture g) {
		v.setGestureVisible(true);
		v.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);
		if (g != null) {
			v.setGesture(g);
		}
		v.setGestureColor(mColor);
		v.setGestureStrokeWidth(mGestureStrokeWidth);
	}

	protected void showError(Throwable e) {
		try {
			Toast.makeText(this, e.getMessage() + "", Toast.LENGTH_SHORT).show();
		} catch (Throwable t) {
			Log.d(TAG, "showError " + t);
		}
	}

	@Override
	protected void zoom(ImageViewTouch d, boolean b) {
		super.zoom(d, b);
		//
		float scale = d.getScale();
		if (Debug.DEBUG) {
			Log.d(TAG, "zoom in: " + b + " scale: " + scale);
		}
		// setViewMode(scale > 1.0f);
	}

	public String getPeer() {
		return mPeer;
	}

	protected void doCompose() {
		mKeyDone.setText(R.string.keyboard_key_send);
		mKeyDone.setTag(R.string.keyboard_key_send);
		
		//
		currentDocument = null;
		mPeer = null;
		//
		mDocument.setViewMode(false);
		updateOverlay(false);
		
		mToolbar.setVisibility(View.VISIBLE);
		mToolbar.setEnabled(true);
		
		mKeyboard.setVisibility(View.VISIBLE);
		mKeyboard.setEnabled(true);
		mKeyboard2.setVisibility(View.GONE);
		mKeyboard2.setEnabled(false);
		//
		mBusy.setVisibility(View.GONE);
		
		//
		updateTitle(getString(R.string.application_desc), R.drawable.send16);

		//
		mDocument.setVisibility(View.VISIBLE);
		//mList.setVisibility(View.GONE);
		//
		mPeer = null;
		clearDocument();
	}

	private void saveIfChanged(Document c) {
		if (c == null) {
			return;
		}
		final boolean changed = c.isChanged();
		if (Debug.DEBUG) {
			Log.d(TAG, "changed: " + changed);
		}
		if (changed) {
			try {
				c.save(mDocument);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	protected void doChat(String peer, int status) {
		mKeyDone.setText(R.string.keyboard_key_close);
		mKeyDone.setTag(R.string.keyboard_key_close);
		//
		//TODO view gone/visible
		mDocument.setVisibility(View.VISIBLE);
		
		//
		loadDocument(peer, status);
	}

	private void updateTitle(String s, int icon) {
		mTitle.setText(s);
		mTitle.setCompoundDrawablesWithIntrinsicBounds(
				icon, 0, 0,
				R.drawable.divider_horizontal_light);
	}
	
	protected void doBack() {
		// TODO Auto-generated method stub
	}
}